package com.haixiaoke.saas.repository.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import com.haixiaoke.saas.repository.dto.ApartmentBillDTO;
import com.haixiaoke.saas.repository.exception.BizException;
import com.haixiaoke.saas.repository.mapper.ApartmentBillMapper;
import com.haixiaoke.saas.repository.mapper.ApartmentItemRuleRefMapper;
import com.haixiaoke.saas.repository.pojo.*;
import com.haixiaoke.saas.repository.service.*;
import com.haixiaoke.saas.repository.util.GsonUtil;
import com.haixiaoke.saas.repository.vo.ApartmentBillVo;
import com.haixiaoke.saas.repository.vo.SubscribeVo;
import com.mzj.saas.commons.StatusCode;
import com.mzj.saas.commons.enums.BacklogItemTypeEnum;
import com.mzj.saas.sdk.pay.vo.WxPay;
import com.mzj.saas.sdk.pay.wxpay.WxPayRequest;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 公寓账单Service业务层处理
 *
 * @author qixi
 * @date 2023-06-16
 */
@Service
@Slf4j
public class ApartmentBillServiceImpl implements IApartmentBillService {
    @Autowired
    private ApartmentBillMapper apartmentBillMapper;
    @Autowired
    private IApartmentChargeItemUsageService apartmentChargeItemUsageService;
    @Autowired
    private IApartmentOrderChargeItemService apartmentOrderChargeItemService;
    @Autowired
    private ApartmentItemRuleRefMapper apartmentItemRuleRefMapper;
    @Autowired
    private IApartmentItemRuleRefService apartmentItemRuleRefService;
    @Autowired
    private IApartmentBillRuleService apartmentBillRuleService;
    @Autowired
    private IOrderService orderService;

    @Autowired
    private IHousingAllocationService housingAllocationService;

    @Autowired
    private ISubscribeService subscribeService;
    @Autowired
    private IAppUserService appUserService;
    @Autowired
    private IBacklogItemService iBacklogItemService;

    @Value("${apartment.pay}")
    private boolean apartmentPay;


    /**
     * 查询公寓账单
     *
     * @param billId 公寓账单主键
     * @return 公寓账单
     */
    @Override
    public ApartmentBill selectApartmentBillByBillId(String billId) {
        return apartmentBillMapper.selectApartmentBillByBillId(billId);
    }

    /**
     * 查询公寓账单列表
     *
     * @param apartmentBill 公寓账单
     * @return 公寓账单
     */
    @Override
    public List<ApartmentBill> selectApartmentBillList(ApartmentBill apartmentBill) {
        return apartmentBillMapper.selectApartmentBillList(apartmentBill);
    }

    /**
     * 新增公寓账单
     *
     * @param apartmentBill 公寓账单
     * @return 结果
     */
    @Override
    public int insertApartmentBill(ApartmentBill apartmentBill) {
        apartmentBill.setCreateTime(new Date());
        return apartmentBillMapper.insertApartmentBill(apartmentBill);
    }

    /**
     * 修改公寓账单
     *
     * @param apartmentBill 公寓账单
     * @return 结果
     */
    @Override
    public int updateApartmentBill(ApartmentBill apartmentBill) {
        apartmentBill.setUpdateTime(new Date());
        return apartmentBillMapper.updateApartmentBill(apartmentBill);
    }

    /**
     * 批量删除公寓账单
     *
     * @param billIds 需要删除的公寓账单主键
     * @return 结果
     */
    @Override
    public int deleteApartmentBillByBillIds(String[] billIds) {
        return apartmentBillMapper.deleteApartmentBillByBillIds(billIds);
    }

    /**
     * 删除公寓账单信息
     *
     * @param billId 公寓账单主键
     * @return 结果
     */
    @Override
    public int deleteApartmentBillByBillId(String billId) {
        return apartmentBillMapper.deleteApartmentBillByBillId(billId);
    }

    @Override
    public List<ApartmentBill> selectApartmentBillByOrderId(String orderId) {
        return apartmentBillMapper.selectApartmentBillByOrderId(orderId);
    }

    @Override
    public ApartmentBill selectApartmentBillByOrderIdOne(String orderId) {
        return apartmentBillMapper.selectApartmentBillByOrderIdOne(orderId);
    }

    @Override
    public List<ApartmentBillVo> selectApartmentBillVoList(ApartmentBill apartmentBill) {

        List<ApartmentBillVo> vos = apartmentBillMapper.selectApartmentBillVoList(apartmentBill);
        for (ApartmentBillVo vo : vos) {
            String billId = vo.getBillId();
            List<ApartmentChargeItemUsage> usages = apartmentChargeItemUsageService.selectApartmentChargeItemUsageByBillId(billId);
            vo.setUsages(usages);
        }
        return vos;
    }

    /**
     * 审核账单
     *
     * @param apartmentBill 账单对象
     */
    @Override
    public int examineApartmentBill(ApartmentBill apartmentBill) {
        List<ApartmentChargeItemUsage> usages = apartmentBill.getUsages();
        String billId = apartmentBill.getBillId();
        String orderId = apartmentBill.getOrderId();
        Long ruleId = apartmentBill.getRuleId();
        String houseId = apartmentBill.getHouseId();
        BigDecimal totalCost = new BigDecimal("0");

        // bug 2358
        if (usages.size() == 0) {
            throw new BizException("请添加账单类型");
        }

        //校验收费项目名称是否重复
        List<String> isNegative = usages.stream().filter(m->(m.getUnitPrice() < 0 || m.getNumber() < 0))
                .map(ApartmentChargeItemUsage::getItemName).collect(Collectors.toList());
        if (CollectionUtil.isNotEmpty(isNegative)){
            throw new BizException("单价/用量 不能为负数");
        }


        for (ApartmentChargeItemUsage usage : usages) {
            if (ObjectUtil.isNull(usage.getUsageId())) {
                usage.setBillId(billId);
                // 新增收费项目
                addChargeItem(usage, orderId, ruleId);
            }
            Double unitPrice = usage.getUnitPrice();
            Double number = usage.getNumber();

            BigDecimal unitPriceBig = new BigDecimal(unitPrice.toString());
            // 计算总金额 向上取整
            BigDecimal multiply = unitPriceBig.multiply(new BigDecimal(number.toString()))
                    .setScale(2, RoundingMode.UP);
            double v = multiply.doubleValue();
            usage.setTotalPrice(v);
            totalCost = totalCost.add(multiply);
            apartmentChargeItemUsageService.updateApartmentChargeItemUsage(usage);
        }
        // 向上取整
        totalCost = totalCost.setScale(2, RoundingMode.UP);
        apartmentBill.setBillCost(totalCost.doubleValue());
        apartmentBill.setAuditStatus(ApartmentBill.BILL_AUDIT_STATUS_TRUE);


        // 审核账单完成之后推送服务消息
        Order order = orderService.selectOrderById(orderId);
        String name = order.getResidenceCommunityName() + order.getBuildingNumName() + "栋" +
                order.getUnitName() + "单元" + order.getRoomNumber();
        SubscribeVo subscribeVo = new SubscribeVo();
        subscribeVo.setProjectName(name);
        subscribeVo.setCost(totalCost.doubleValue());
        subscribeVo.setBillTypeName("账单");
        subscribeVo.setLastPaymentTime(apartmentBill.getPayDeadline());
        subscribeVo.setBillStartTime(apartmentBill.getStartTime());
        subscribeVo.setBillEndTime(apartmentBill.getEndTime());

        String reservationId = apartmentBill.getReservationId();
        String openId = appUserService.selectOpenIdById(reservationId);
        subscribeVo.setOpenId(openId);
        subscribeVo.setFlag(2);

        subscribeService.send(subscribeVo);

        // 创建同住人待办事项
        BacklogItem backlogItem = new BacklogItem();
        // 房屋id
        backlogItem.setHouseId(houseId);
        // 如果是公租房取产权单位id，如果是非公租房取物业公司id
        backlogItem.setOrgId(order.getOrgId());
        // 个人组织id
        backlogItem.setUserOrgId(order.getOrgId());
        // 待办事项类型
        backlogItem.setType(BacklogItemTypeEnum.APARTMENT_BILL.getValue());
        // 标题
        backlogItem.setTitle("账单提醒");
        // 内容
        backlogItem.setContent(order.getResidenceCommunityName() + "小区" + order.getBuildingNumName() + "栋" + order.getUnitName() + "单元" + order.getRoomNumber() + "房，您的账单已生成，请尽快缴费。");
        backlogItem.setBusinessStatus("待查看");
        backlogItem.setHandleStatus(0);
        backlogItem.setMatchUserId(order.getReservationId());
        backlogItem.setPhone(order.getReservationPhone());
        // 操作人id
        backlogItem.setOperatorId(order.getReservationId());
        // 操作时间
        backlogItem.setCreateTime(new Date());
        iBacklogItemService.insertBacklogItem(backlogItem);

        return apartmentBillMapper.updateApartmentBill(apartmentBill);
    }

    @Override
    public ApartmentBillVo selectApartmentBillVoByBillId(String billId) {
        ApartmentBillVo vo = apartmentBillMapper.selectApartmentBillVoByBillId(billId);
        List<ApartmentChargeItemUsage> usages = apartmentChargeItemUsageService.selectApartmentChargeItemUsageByBillId(billId);
        vo.setUsages(usages);
        return vo;
    }


    @Override
    public boolean checkBillPayState(List<String> billIds) {
        return apartmentBillMapper.checkBillPayState(billIds) > 0;
    }

    @Override
    public Map<String, Object> apartmentBillWXPay(ApartmentBillDTO dto) {
        List<String> billIds = dto.getBillIds();
        List<ApartmentBill> apartmentBills = this.selectApartmentBillByBillIds(billIds);
        double billCost = apartmentBills.stream().mapToDouble(ApartmentBill::getBillCost).sum();
        Integer amountCount = jodd.typeconverter.Convert.toInteger(NumberUtil.mul(billCost, 100));

        ApartmentBill apartmentBill = apartmentBills.get(0);
        Long orgId = apartmentBill.getOrgId();
        WxPay wxpay = new WxPay(dto.getAppId(), dto.getMchId(), dto.getOutTradeNo(),
                orgId.toString(), apartmentPay ? amountCount : 1,    // todo 金钱单位以分
                "房屋管家-公寓账单支付", dto.getMchApiKey(), dto.getOpenId(),
                dto.getWxNotifyUrl(), dto.getUnifiedApi(),
                dto.getSpbillCreateIp());

        log.info("小程序公寓账单支付的类--WxPay:{}", GsonUtil.toJson(wxpay));
        Map<String, Object> result = new WxPayRequest(wxpay).unifiedByMini();
        if (CollectionUtil.isEmpty(result)) {
            throw new BizException(Integer.valueOf(StatusCode.ERROR_CODE_10011.getErrorCode()), StatusCode.ERROR_CODE_10011.getErrorMsg());
        }
        return result;
    }

    @Override
    public List<ApartmentBill> selectApartmentBillByBillIds(List<String> billIds) {
        return apartmentBillMapper.selectApartmentBillByBillIds(billIds);
    }


    private ApartmentChargeItemUsage addChargeItem(ApartmentChargeItemUsage usage, String orderId, Long ruleId) {
        String itemName = usage.getItemName();
        String itemUnit = usage.getItemUnit();
        Double unitPrice = usage.getUnitPrice();
        // 新增订单独有收费项目
        ApartmentOrderChargeItem item = new ApartmentOrderChargeItem();
        item.setItemName(itemName);
        item.setItemUnit(itemUnit);
        item.setOrderId(orderId);
        item.setCreateTime(new Date());
        apartmentOrderChargeItemService.insertApartmentOrderChargeItem(item);

        ApartmentItemRuleRef ref = new ApartmentItemRuleRef();
        ref.setRuleId(ruleId);
        ref.setItemId(item.getItemId());
        ref.setItemType(ApartmentItemRuleRef.ITEM_TYPE_ORDER_ITEM);
        ref.setItemName(itemName);
        ref.setItemUnit(itemUnit);
        ref.setUnitPrice(unitPrice);
        ref.setCreateTime(new Date());
        apartmentItemRuleRefMapper.insertApartmentItemRuleRef(ref);
        usage.setRefId(ref.getRefId());
        apartmentChargeItemUsageService.insertApartmentChargeItemUsage(usage);
        return usage;
    }


    /**
     * 生成账单任务
     */
    @Transactional
    public void createApartmentBillTask() {
        log.info("生成公寓账单任务 start");
        Date date = new Date();
        date = DateUtil.parse(DateUtil.format(date, "yyyy-MM-dd HH:mm:ss"));
        List<ApartmentBillRule> apartmentBillRule = apartmentBillRuleService.selectApartmentBillRuleByPeriod();
        for (ApartmentBillRule billRule : apartmentBillRule) {
            Long ruleId = billRule.getRuleId();
            String orderId = billRule.getOrderId();
            Order order = orderService.selectOrderById(orderId);
            String houseId = billRule.getHouseId();
            Long payMonth = billRule.getPayMonth();
            Integer closeTime = billRule.getCloseTime();


            HousingAllocation housingAllocation = housingAllocationService.selectHousingAllocationByHouseId(houseId);
            double monthPrice = housingAllocation.getMonthPrice();

            Date billLastTime = billRule.getBillLastTime();
            Date endTime = billRule.getEndTime();

            double rentCost;
            Date billStartTime;
            Date billEndTime;
            double monthNumber;

            // 生成当前时间的账单
            // 校验生成账单时间是否满足租赁周期内
            DateTime lastTime = DateUtil.offsetMonth(billLastTime, payMonth.intValue());
            DateTime lastDateTime = DateUtil.parse(DateUtil.format(lastTime, "yyyy-MM-dd 00:00:00"));
            DateTime parse = DateUtil.endOfDay(endTime);


            // 最后生成时间大于账单结束时间则不生成 或者当前是不是生成月份的时间
            if (apartmentPay && (lastDateTime.getTime() > parse.getTime() || !DateUtil.isSameMonth(date, lastDateTime))) {
                continue;
            }


            // 收费开始时间
            billStartTime = DateUtil.beginOfMonth(lastTime);

            // 收费结束时间
            billEndTime = DateUtil.endOfMonth(DateUtil.offsetMonth(lastTime, payMonth.intValue() - 1));

            BigDecimal monthPriceBig = BigDecimal.valueOf(monthPrice);
            DateTime outTime = DateUtil.offsetMonth(lastTime, payMonth.intValue());
            // 判断是否是最后一期账单 如是则将最后一月的 天数时间*日单价
            if (outTime.getTime() > parse.getTime()) {
                long monthNum = DateUtil.betweenMonth(billStartTime, parse, false);
                billEndTime = parse;
                monthNumber = Convert.toInt(monthNum + 1);
                int monthDays = DateUtil.lengthOfMonth(DateUtil.month(parse) + 1, false);
                int day = DateUtil.dayOfMonth(parse);

                BigDecimal dayCost = monthPriceBig.divide(new BigDecimal(monthDays), 2, RoundingMode.UP);
                double lastMonthCost = dayCost.multiply(new BigDecimal(day)).setScale(2, RoundingMode.UP).doubleValue();

                // 租金= 付几*月租金 (如果最后账单不足则是 (月租金*剩余月数-1)+最后一月天数/月租金*实住天数)
                rentCost = monthPriceBig.multiply(BigDecimal.valueOf(monthNum)).add(BigDecimal.valueOf(lastMonthCost))
                        .setScale(2, RoundingMode.UP).doubleValue();
            } else {
                rentCost = monthPriceBig.multiply(BigDecimal.valueOf(payMonth)).setScale(2, RoundingMode.UP).doubleValue();
                monthNumber = payMonth.intValue();
            }
            Date payDeadline = DateUtil.endOfDay(DateUtil.offsetDay(DateUtil.beginOfMonth(billStartTime), closeTime - 1));
            billEndTime = DateUtil.endOfDay(billEndTime);


            ApartmentBill apartmentBill = new ApartmentBill();
            String billId = "ZD" + System.currentTimeMillis() + RandomUtils.nextInt(1000, 10000);
            apartmentBill.setBillId(billId);
            apartmentBill.setRuleId(ruleId);
            apartmentBill.setOrderId(orderId);
            apartmentBill.setOrgId(order.getOrgId());
            apartmentBill.setHouseId(houseId);

            // 清除毫秒值 保证时间
            billStartTime = com.mzj.saas.commons.util.DateUtils.getPreciseTime(billStartTime);
            apartmentBill.setStartTime(billStartTime);

            billEndTime = com.mzj.saas.commons.util.DateUtils.getPreciseTime(billEndTime);
            apartmentBill.setEndTime(billEndTime);

            payDeadline = com.mzj.saas.commons.util.DateUtils.getPreciseTime(payDeadline);
            apartmentBill.setPayDeadline(payDeadline);

            apartmentBill.setReservationId(billRule.getReservationId());
            apartmentBill.setReservationName(billRule.getReservationName());
            apartmentBill.setReservationPhone(billRule.getReservationPhone());
            apartmentBill.setCreateTime(date);
            int i = apartmentBillMapper.insertApartmentBill(apartmentBill);


            List<ApartmentItemRuleRef> ref = apartmentItemRuleRefService.selectApartmentItemRuleRefByRuleId(ruleId);
            for (ApartmentItemRuleRef apartmentItemRuleRef : ref) {
                Long itemId = apartmentItemRuleRef.getItemId();
                Integer itemType = apartmentItemRuleRef.getItemType();
                // 租金收费项
                if (itemType.equals(ApartmentItemRuleRef.ITEM_TYPE_DEFAULT_ITEM) && ObjectUtil.equals(itemId, 3)) {
                    ApartmentChargeItemUsage usage = new ApartmentChargeItemUsage();
                    usage.setBillId(apartmentBill.getBillId());
                    usage.setRefId(apartmentItemRuleRef.getRefId());
                    usage.setItemName(apartmentItemRuleRef.getItemName());
                    usage.setUnitPrice(apartmentItemRuleRef.getUnitPrice());
                    usage.setNumber(monthNumber);
                    usage.setTotalPrice(rentCost);
                    usage.setItemUnit(apartmentItemRuleRef.getItemUnit());
                    usage.setCreateTime(date);
                    apartmentChargeItemUsageService.insertApartmentChargeItemUsage(usage);
                } else {
                    ApartmentChargeItemUsage usage = new ApartmentChargeItemUsage();
                    usage.setBillId(apartmentBill.getBillId());
                    usage.setRefId(apartmentItemRuleRef.getRefId());
                    usage.setItemName(apartmentItemRuleRef.getItemName());
                    usage.setUnitPrice(apartmentItemRuleRef.getUnitPrice());
                    usage.setItemUnit(apartmentItemRuleRef.getItemUnit());
                    usage.setCreateTime(date);
                    apartmentChargeItemUsageService.insertApartmentChargeItemUsage(usage);
                }
            }
            billRule.setBillLastTime(date);
            apartmentBillRuleService.updateApartmentBillRule(billRule);
        }
        log.info("生成公寓账单任务 end");
    }

    /**
     * 生成退租账单
     *
     * @param orderId
     */
//    @Transactional
    public void createRentWithdrawalBill(String orderId) {

        ApartmentBillRule rule = apartmentBillRuleService.selectApartmentBillRuleByOrderId(orderId);
        if (rule == null) {
            throw new BizException("账单生成规则未找到!");
        }


        ApartmentBill apartmentBill = new ApartmentBill();
        String billId = "ZD" + System.currentTimeMillis() + RandomUtils.nextInt(1000, 10000);
        apartmentBill.setBillId(billId);
        apartmentBill.setRuleId(rule.getRuleId());
        apartmentBill.setOrderId(orderId);
        apartmentBill.setOrgId(rule.getOrgId());
        apartmentBill.setHouseId(rule.getHouseId());
        apartmentBill.setStartTime(rule.getStartTime());
        apartmentBill.setEndTime(rule.getEndTime());

        Date date = new Date();
        DateTime dateTime = DateUtil.endOfMonth(date);
        apartmentBill.setPayDeadline(dateTime);
        apartmentBill.setReservationId(rule.getReservationId());
        apartmentBill.setReservationName(rule.getReservationName());
        apartmentBill.setReservationPhone(rule.getReservationPhone());
        apartmentBill.setCreateTime(date);
        apartmentBillMapper.insertApartmentBill(apartmentBill);

        List<ApartmentItemRuleRef> ref = apartmentItemRuleRefService.selectApartmentItemRuleRefByRuleId(rule.getRuleId());
        for (ApartmentItemRuleRef apartmentItemRuleRef : ref) {
            Long itemId = apartmentItemRuleRef.getItemId();
            Integer itemType = apartmentItemRuleRef.getItemType();
            // 租金收费项
            if (itemType.equals(ApartmentItemRuleRef.ITEM_TYPE_DEFAULT_ITEM) && ObjectUtil.equals(itemId, 3)) {
                // 退租租金账单金额 用量默认不给默认值
                ApartmentChargeItemUsage usage = new ApartmentChargeItemUsage();
                usage.setBillId(apartmentBill.getBillId());
                usage.setRefId(apartmentItemRuleRef.getRefId());
                usage.setItemName(apartmentItemRuleRef.getItemName());
                usage.setUnitPrice(apartmentItemRuleRef.getUnitPrice());
                usage.setNumber(0.0);
                usage.setTotalPrice(0.0);
                usage.setItemUnit(apartmentItemRuleRef.getItemUnit());
                usage.setCreateTime(date);
                apartmentChargeItemUsageService.insertApartmentChargeItemUsage(usage);
            } else {
                ApartmentChargeItemUsage usage = new ApartmentChargeItemUsage();
                usage.setBillId(apartmentBill.getBillId());
                usage.setRefId(apartmentItemRuleRef.getRefId());
                usage.setItemName(apartmentItemRuleRef.getItemName());
                usage.setUnitPrice(apartmentItemRuleRef.getUnitPrice());
                usage.setItemUnit(apartmentItemRuleRef.getItemUnit());
                usage.setCreateTime(date);
                apartmentChargeItemUsageService.insertApartmentChargeItemUsage(usage);
            }
        }
    }

}
